package edu.asu.sapa.sas;

import java.util.*;

import edu.asu.sapa.ground.Operator;
import edu.asu.sapa.ground.Problem;
import edu.asu.sapa.ground.PropDB;
import edu.asu.sapa.ground.State;
import edu.asu.sapa.lifted.LiftedGoal;

/**
 * This translation assumes a one-to-one relationship between atoms and STRIPS
 * predicates. That is there cannot exist more than one atom that corresponds to
 * a STRIPS predicate.
 * 
 * @author J. Benton
 * 
 */
public class PSPInterface {
	SAS sas;

	/** */
	protected HashMap<Integer, Operator> sasToStripsAct = new HashMap<Integer, Operator>();

	/** */
	protected HashMap<Integer, SASOperator> stripsToSasAct = new HashMap<Integer, SASOperator>();

	/** */
	protected HashMap<Atom, Integer> sasToStripsPred = new HashMap<Atom, Integer>();

	/** */
	protected HashMap<Integer, Atom> stripsToSasPred = new HashMap<Integer, Atom>();

	private Problem prob;

	/**
	 * Must have already grounded actions in the "Grounding" object. The list of
	 * GActions is unused but instead serves as a reminder to ground the domain
	 * before creating this object.
	 * 
	 * @param sas
	 *            The SAS version of the domain.
	 * @param gAction
	 *            The grounded actions from a STRIPS version of the domain.
	 */
	public PSPInterface(SAS sas, ArrayList<Operator> gActions, Problem prob) {
		this.sas = sas;
		this.prob = prob;
		// generate mapping for operators
		for (SASOperator op : sas.getOperators()) {

			Operator stripsAct = Problem.actSigMap.get(op.getName());
			if (stripsAct == null) {
				throw new NullPointerException("Action (" + op.getName()
						+ ") does not exist in STRIPS.");
			}
			
			// FIXME try sending in nulls for now
			op.setCost(stripsAct.getCost(null, null));
			sasToStripsAct.put(op.getID(), stripsAct);
			stripsToSasAct.put(stripsAct.id, op);
		}

		// generate mapping for preds/atoms
		for (Atom a : sas.getAtoms()) {
			Integer stripsID = Problem.predSigMap.get(a.getName()).id;
			
			if (stripsID == null) {
				throw new RuntimeException("Predicate (" + a.getName()
						+ ") does not exist in STRIPS.");
			}

			a.setPredID(stripsID);
			sasToStripsPred.put(a, stripsID);
			stripsToSasPred.put(stripsID, a);
		}
		
		// set reward for the individual goals
		for (int i = 0;i<prob.lifted.numGoal();i++) {
			LiftedGoal lg = prob.lifted.getGoal(i);
			setUtility(Problem.goalToProposition.get(lg).id, lg.reward);
		}
		
		// set reward for goal utility dependencies
	}

	public Problem getProb() {
		return prob;
	}
	
	public void setUtility(int stripsID, float util) {
		stripsToSasPred.get(stripsID).setUtility(util);
	}

//	public void setCost(int stripsID, float cost) {
//		stripsToSasAct.get(stripsID).setCost(cost);
//	}

	/**
	 * Returns a List of integers, ordered by variable ID in the SAS file. Each
	 * integer represents the new value for the particular state value given the
	 * StateInfo object.
	 * 
	 * @param si
	 *            A STRIPS-like state.
	 */
	public List<Integer> getNewVariableValues(State si) {
		Integer values[] = new Integer[sas.getVariables().size()];

		// initialize all of the values to <none of these>
		for (Variable v : sas.getVariables()) {
			values[v.getID()] = v.getNoneValue();
		}

		PropDB preds = si.getPropDB();
		// search atoms to see if one exists in the current state
		for (Atom a : sas.getAtoms()) {
			// if the state contains this predicate value...
			// FIXME might be broke...?
			if (preds.getValueAt(a.getPredID(),si.time,true)) {
				// then get the associated variable value...
				values[a.getVariable().getID()] = a.getValue();
			}
		}

		return Arrays.asList(values);
	}

	/**
	 * Return a list of predicate IDs in the STRIPS formulation from a list of IDs in
	 * the SAS formulation.
	 * 
	 * @param atoms
	 * @return A list of predicate IDs.
	 */
	public ArrayList<Integer> convertAtoms(List<Atom> atoms) {
		ArrayList<Integer> predIDs = new ArrayList<Integer>();
		for (Atom a : atoms) {
			if ((sasToStripsPred.get(a)) != null) {
				predIDs.add(sasToStripsPred.get(a));
			}
		}
		return predIDs;
	}
	
	/**
	 * Converts a list of predicate IDs to an ordered list of variable values.
	 * A variable will have a value of -1 if no predicate exists for it in the given list.
	 * @param preds
	 * @return
	 */
	public List<Integer> convertPreds(List<Integer> preds) {
		Integer[] varValues = new Integer[sas.numVars];
		
		for (int i=0;i<sas.numVars;i++) {
			// initially set to "none of these"
			varValues[i] = sas.getVariables().get(i).getDomain()-1;
		}
		
		for (Integer pred : preds) {
			Atom a = stripsToSasPred.get(pred);
			if (a != null) {
				varValues[a.getVariable().getID()] = a.getValue();
			} else 
				throw new RuntimeException("Cannot convert a predicate to an atom (Some change in state?  Or SAS/STRIPS files out of sync?)");
		}
		
//		for (int i=0;i<varValues.length;i++) {
//			if (varValues[i] == -1) {
//				System.out.println(";; A problem with finding SAS " + sas.getVariables().get(i).name);
//			}
//		}
		
		return Arrays.asList(varValues);
	}
	
	/**
	 * Return the set of STRIPS ID goals.
	 * @param atoms
	 * @return
	 */
	public ArrayList<Integer> convertGoals(List<Atom> atoms) {
		ArrayList<Integer> predIDs = new ArrayList<Integer>();
		for (Atom a : atoms) {
			Integer id;
			// make sure it exists (this should always pass)
			if ((id=sasToStripsPred.get(a)) != null) {
				predIDs.add(sasToStripsPred.get(a));
			}
		}
		return predIDs;		
	}
	
	/**
	 * Converts an SAS operator (id) to a GAction (object).
	 * @param id
	 * @return
	 */
	public Operator convertOperator(int id) {
		return sasToStripsAct.get(id);
	}

	/**
	 * Converts an SAS operator (object) to a GAction (object).
	 * @param op
	 * @return
	 */
	public Operator convertOperator(SASOperator op) {
		return sasToStripsAct.get(op.getID());
	}

	
	public Atom getAtom(int predID) {
		return stripsToSasPred.get(predID);
	}
}
